package br.com.forca.controller;

import java.io.IOException;
import java.util.List;
import br.com.forca.model.factory.AbstractPlayerFactory;
import br.com.forca.model.factory.AbstractWordFactory;
import br.com.forca.model.factory.PlayerFactory;
import br.com.forca.model.factory.WordFactory;
import br.com.forca.model.jogador.Playable;
import br.com.forca.model.service.WordService;
import br.com.forca.model.service.WordServiceImpl;
import br.com.forca.model.word.Readable;
import br.com.forca.model.word.Word;
import br.com.forca.utils.IOUtils;
import br.com.forca.utils.PlayerUtils;
import br.com.forca.utils.WordUtils;
import br.com.forca.view.ForcaInterface;
import br.com.forca.view.UserInterface;

public class Forca {

	// CRIACAO DAS CLASSES DE INTERESSE DO JOGO
	private WordService wordService;
	private IOUtils io;
	private WordUtils wu;
	private PlayerUtils pu;
	private UserInterface ui;
	private ForcaInterface fi;
	private AbstractWordFactory wordFactory;
	private AbstractPlayerFactory playerFactory;
	private List<Playable> listaDeJogadores;
	private List<Readable> listaDePalavras;
	private Readable w;

	// CONSTANTES CRIADAS PARA CONTROLE
	private static final int QTDE_MAX_ERROS = 6;
	private static final int QTDE_MAX_PLAYERS = 2;

	public Forca() {
		this.wordService = new WordServiceImpl();
		this.io = new IOUtils();
		this.wu = new WordUtils();
		this.ui = new UserInterface();
		this.fi = new ForcaInterface();
		this.pu = new PlayerUtils();
		this.w = new Word();
		this.wordFactory = new WordFactory();
		this.playerFactory = new PlayerFactory();
		this.listaDeJogadores = playerFactory.createJogadores();
		this.listaDePalavras = wordFactory.createPalavras();
	}

	
	
	public void start() throws IOException {

		// CRIACAO DE VARIAVEIS PARA CONTROLE
		boolean jogar = true;
		Tentativa tentativa = new Tentativa();

		// IMPRESSAO DO INICIO DO JOGO
		ui.imprimeInicioJogo();
		ui.imprimeMensagemEmBranco();
		configuraQuantidadeDeJogadores();

		
		do {
			// TENTA RODAR O CÓDIGO, CASO NÃO ENCONTRE O ERRO
			// 'StringIndexOutOfBoundsException'
			w = wu.retornaPalavra(listaDePalavras);

			try {
				// VARIÁVEL QUE ARMAZENA O TAMANHO DA PALAVRA
				int tamanhoDaPalavra = w.getPalavraAtual().length();

				// IMPRIME INSTRUCOES PARA O JOGADOR NO TERMINAL
				ui.imprimeInstrucoes(pu.retornaNomes(listaDeJogadores));
				ui.imprimeMensagemEmBranco();
				ui.imprimeDicas(tamanhoDaPalavra, wu.retornaDica(w));
				boolean alguemAcertou = false;
				// LOOP PARA REALIZAR AS JOGADAS ENTRE OS PARTICIPANTES;
				do {
					for (int playerIndex = 0; playerIndex < listaDeJogadores.size(); playerIndex++) {
						tentativa = jogada(w, playerIndex, tentativa);
						if (tentativa.isAcerto() == true) {
							ui.imprimeResultadoVitoria(listaDeJogadores.get(playerIndex).getNome(),	w.getPalavraAtual());
							configuraContinuacaoDoJogo(listaDeJogadores.get(playerIndex).getNome());
							alguemAcertou = true;
							jogar = true;
							break;
						} else {
							jogar = true;
						}
					}
				} while (alguemAcertou == false);
			} catch (StringIndexOutOfBoundsException e) {
				ui.imprimeMensagem("\nErro no programa. Possivelmente, você digitou alguma tecla inválida.");
			}

		} while (jogar);
	}

	
	
	
	private Tentativa jogada(Readable w, int indicePlayer, Tentativa guess) {

	  
		int tamanhoDaPalavra = w.getPalavraAtual().length();
		List<String> listaDoAlfabeto = guess.getAlfabetoList();

		// ARRAY PARA CONTER AS LETRAS DA PALAVRA SORTEADA E LOOP PARA ARMAZENAR AS LETRAS NO MESMO
		guess.setArrayLetrasAcertadas(wordService.palavraStringToArrayChar(w.getPalavraAtual()));
		ui.imprimeMensagemEmBranco();

		int erro = listaDeJogadores.get(indicePlayer).getNumeroDeErros();

		// ARRAY PARA ARMAZENAMENTO DAS LETRAS CORRETAS (AS QUE O JOGADOR DIGITA
		// E ESTÃO NA PALAVRA SORTEADA)
		char letraCerta[] = guess.getArrayLetrasCertas();

		if (erro < QTDE_MAX_ERROS) {
			// IMPRIME O ALFABETO VÁLIDO PARA O JOGO
			ui.imprimeAlfabeto(guess.getAlfabetoList());

			// SOLICITA A ENTRADA DO USUARIO
			String entradaDoUsuario = io.getUserInputString("\n"+ listaDeJogadores.get(indicePlayer).getNome()+ ", digite uma Letra ou tente acertar a palavra!!!");
			char letraDigitada;
			
			if (entradaDoUsuario.length() > 1) {
				if ((wu.comparaPalavras(entradaDoUsuario, w.getPalavraAtual())) == 3) {
					ui.imprimeComparacaoDePalavras((wu.comparaPalavras(entradaDoUsuario, w.getPalavraAtual())), entradaDoUsuario);
					guess.setAcerto(true);
					return guess;
				} else if (((wu.comparaPalavras(entradaDoUsuario, w.getPalavraAtual())) != 3)) {
					ui.imprimeComparacaoDePalavras((wu.comparaPalavras(entradaDoUsuario, w.getPalavraAtual())), entradaDoUsuario);
				}
			}

			if (entradaDoUsuario.length() == 1) {
				letraDigitada = entradaDoUsuario.charAt(0);
			} else {
				letraDigitada = io.getUserInputChar("\n"+ listaDeJogadores.get(indicePlayer).getNome()+ ", digite uma Letra!!!");
			}

			/* A LETRA DIGITADA ('char letra', LOGO ABAIXO)PERCORRERÁ OS VALORES
			 * DE UM ARRAY (letras[]), UM A UM, PARA ENCONTRAR CARACTERES
			 * IGUAIS.PORTAN1TO, VARIÁVEL 'erroLet' ARMAZENA O VALOR QUANDO A
			 * LETRA NÃO SE ENCONTRA NO ARRAY */

			int erroLet = 0;

			// CRIA VARIÁVEL PARA ARMAZENAR OS VALORES ACERTADOS PELO JOGADOR
			String novaPalavra = "";
			int qtdeDeVezes = 0;
			String letraString = "";

			for (int x = 0; x < tamanhoDaPalavra; x++) {

				/*
				 * SE A LETRA DIGITADA SE ENCONTRA NA POSIÇÃO x DO ARRAY
				 * 'letras[]', O VALOR É ARMAZENADO NO MESMO ÍNDICE DO ARRAY
				 * 'letraCerta'. CASO CONTRÁRIO, INCREMENTA O VALOR 'erroLet'
				 */

				if (wu.trataLetras(letraDigitada + "") == wu.trataLetras(guess.getArrayLetrasAcertadas()[x] + "")) {
					letraCerta[x] = letraDigitada;
					letraString = letraDigitada + "";
					listaDoAlfabeto.remove(letraString.toUpperCase());
					qtdeDeVezes++;

				} else {
					erroLet++;
					listaDoAlfabeto.remove(letraString.toUpperCase());

				}
				novaPalavra += wordService.verificaLetrasNaPalavra(letraCerta[x], guess.getArrayLetrasAcertadas()[x], novaPalavra);
				/*
				 * SE JÁ CONTIVER A LETRA CERTA NA MESMA POSIÇÃO EM QUE A LETRA
				 * SE ENCONTRA NA PALAVRA SORTEADA, A LETRA É IMPRESSA E SEU
				 * VALOR É ADICIONADO À VARIÁVEL QUE CONTÉM OS DADOS CERTOS
				 * DIGITADOS PELO JOGADOR ('novaPalavra'). CASO CONTRÁRIO,
				 * IMPRIME UM UNDERLINE (_)
				 */
			}
			
			guess.setNovaPalavra(novaPalavra);
		  			
			ui.imprimeMensagem(wordService.retornaOcorrenciaDeLetras(letraDigitada, qtdeDeVezes));
			ui.imprimeMensagemEmBranco();
			ui.imprimeMensagem(novaPalavra);

			if (erroLet >= tamanhoDaPalavra) {
				erro = listaDeJogadores.get(indicePlayer).incrementaNumeroDeErros();
			}

			/*
			 * CASO AS LETRAS DIGITADAS FORMEM A PALAVRA CORRETA, É MOSTRADA UMA
			 * PARABENIZAÇÃO E É SETADA A VARIÁVEL 'tentativa' PARA 'false',
			 * TERMINANDO O JOGO ATUAL
			 */

			if (w.getPalavraAtual().equals(novaPalavra.replaceAll(" ", ""))) {
				ui.imprimeResultadoVitoria(listaDeJogadores.get(indicePlayer).getNome(), w.getPalavraAtual());
				guess.setAcerto(true);

			} else {
				fi.desenhaController(erro);
				ui.imprimeErros(((QTDE_MAX_ERROS) - (listaDeJogadores.get(indicePlayer).getNumeroDeErros())), listaDeJogadores.get(indicePlayer).getNome());
			}
		} else {			
			ui.imprimeResultadoDerrota(listaDeJogadores.get(indicePlayer).getNome(), w.getPalavraAtual());			
		}

		return guess;
	}

	
	
	
	
	private void configuraQuantidadeDeJogadores() {

		// ESTE MENU IRA INFORMAR AS OPCOES DE JOGO AO USUARIO (1 OU 2 JOGADORES)
		ui.imprimeMenuPlayer();

		Playable player1;
		Playable player2;

		int opcaoMenuPlayer = (io.getUserInputNumber(("\nDigite um número: ")));
		boolean continuar = true;

		do {
			if (opcaoMenuPlayer == 1) {

				ui.imprimeMensagem("\n##JOGADOR 1##");
				player1 = playerFactory.createJogador(io.getUserInputString("Digite o nome do Jogador 1: "));
				listaDeJogadores.add(player1);
				continuar = false;

			} else if (opcaoMenuPlayer == QTDE_MAX_PLAYERS) {
				ui.imprimeMensagem("\n##JOGADOR 1##");
				player1 = playerFactory.createJogador(io.getUserInputString("Digite o nome do Jogador 1: "));
				listaDeJogadores.add(player1);

				ui.imprimeMensagem("\n##JOGADOR 2##");
				player2 = playerFactory.createJogador(io.getUserInputString("Digite o nome do Jogador 2: "));
				listaDeJogadores.add(player2);
				continuar = false;

			} else if (opcaoMenuPlayer > QTDE_MAX_PLAYERS) {
				ui.imprimeMensagem("\nEntrada inválida, Limite de Jogadores Ultrapassados!!!");
				continuar = true;
				opcaoMenuPlayer = (io.getUserInputNumber(("\nDigite um número: ")));
			}

		} while (continuar);

	}

	private boolean configuraContinuacaoDoJogo(String nomeDoJogador) {

		char jogarNovamente = io.getUserInputChar("\n" + nomeDoJogador	+ ", deseja Jogar novamente?" + "\n" + "###### Digite 'S' ou 'N' ######");

		if (jogarNovamente == 'S' || jogarNovamente == 's') {
			return true;

		} else {
			ui.imprimeFimDoJogo();
			return false;
		}
	}

}
